import * as THREE from '../../build/three.module.js';

// 场景、相机、渲染器
let scene, camera, renderer;

// 背景材质，背景贴图集合
let materialBg, mapsBg = {};

// 预乘前景贴图，非预乘前景贴图，前景元素材质列表
const mapsFgPre = [], mapsFgNoPre = [], materials = [];

// 当前前景贴图组，当前前景贴图下标
let currentMaps = mapsFgNoPre, currentMapIndex = 0;

// 工具函数
// 生成文本标签材质
const generateLabelMaterial = (text, bg) => {
  const canvas = document.createElement('canvas');
  canvas.width = 128;
  canvas.height = 32;
  const ctx = canvas.getContext('2d');

  ctx.fillStyle = bg;
  ctx.fillRect(0, 0, 128, 32);

  ctx.fillStyle = 'white';
  ctx.font = '12pt arial bold'
  ctx.fillText(text, 8, 22);

  const map = new THREE.CanvasTexture(canvas);
  
  return new THREE.MeshBasicMaterial({ map: map, transparent: true });
}

// 事件处理函数
// 背景图片点击处理
const handleBgClick = e => {
  materialBg.map = mapsBg[e.currentTarget.id];
}
// 前景图片点击处理
const handleFgClick = index => {
  for(let i = 0; i < materials.length; i += 1) {
    materials[i].map = currentMaps[index]
  }
  currentMapIndex = index;
}
// 混合方程点击处理
const handleEquationClick = e => {
  for(let i = 0; i < materials.length; i += 1) {
    materials[i].blendEquation = THREE[e.currentTarget.dataset.equation];
  }

  const equations = document.getElementsByClassName('equation');
  for(let i = 0; i < equations.length; i += 1) {
    const element = equations[i];
    if (element.id === e.currentTarget.id) {
      element.style.backgroundColor = 'darkorange';
    } else {
      element.style.backgroundColor = 'transparent';
    }
  }
}
// 预乘开关
const handlePremultiplyClick = e => {
  if (e.currentTarget.id === 'btn_pre') {
    currentMaps = mapsFgPre;
  } else {
    currentMaps = mapsFgNoPre;
  }
  for(let i = 0; i < materials.length; i += 1) {
    materials[i].map = currentMaps[currentMapIndex];
  }
  const premultips = document.getElementsByClassName('premultiply');
  for(let i = 0; i < premultips.length; i += 1) {
    if (premultips[i].id === e.currentTarget.id) {
      premultips[i].style.backgroundColor = 'darkorange';
    } else {
      premultips[i].style.backgroundColor = 'transparent';
    }
  }
}

// 初始化函数
const init = () => {
  // 场景
  scene = new THREE.Scene();

  // 相机
  camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 1, 10000);
  camera.position.z = 700;

  // 增加 bg_1，bg_1 背景贴图，补齐背景选项
  const canvasBg1 = document.createElement('canvas');
  canvasBg1.width = canvasBg1.height = 128;
  let ctx = canvasBg1.getContext('2d');

  ctx.fillStyle = '#eee';
  ctx.fillRect(0, 0, 128, 128);
  ctx.fillStyle = '#999';
  ctx.fillRect(0, 0, 64, 64);
  ctx.fillStyle = '#555';
  ctx.fillRect(32, 32, 32, 32);
  ctx.fillStyle = '#999';
  ctx.fillRect(64, 64, 64, 64);
  ctx.fillStyle = '#777';
  ctx.fillRect(96, 96, 32, 32);

  document.getElementById('bg_1').appendChild(canvasBg1);

  const canvasBg2 = document.createElement('canvas');
  canvasBg2.width = canvasBg2.height = 128;
  ctx = canvasBg2.getContext('2d');
  ctx.fillStyle = '#444';
  ctx.fillRect( 0, 0, 128, 128 );
  ctx.fillStyle = '#000';
  ctx.fillRect( 0, 0, 64, 64 );
  ctx.fillStyle = '#111';
  ctx.fillRect( 32, 32, 32, 32 );
  ctx.fillStyle = '#000';
  ctx.fillRect( 64, 64, 64, 64 );
  ctx.fillStyle = '#222';
  ctx.fillRect( 96, 96, 32, 32 );

  document.getElementById('bg_2').appendChild(canvasBg2);

  // 准备背景贴图
  const textureLoader = new THREE.TextureLoader();

  const mapBg0 = new THREE.CanvasTexture(canvasBg1);
  mapBg0.wrapS = mapBg0.wrapT = THREE.RepeatWrapping;
  mapBg0.repeat.set(128, 64);
  mapsBg['bg_1'] = mapBg0;

  const mapBg1 = new THREE.CanvasTexture(canvasBg2);
  mapBg1.wrapS = mapBg1.wrapT = THREE.RepeatWrapping;
  mapBg1.repeat.set(128, 64);
  mapsBg['bg_2'] = mapBg1;

  const mapBg2 = textureLoader.load('../../textures/disturb.jpg');
  mapBg2.wrapS = mapBg2.wrapT = THREE.RepeatWrapping;
  mapBg2.repeat.set(8, 4);
  mapsBg['bg_0'] = mapBg2;
  
  const mapBg3 = textureLoader.load('../../textures/crate.gif');
  mapBg3.wrapS = mapBg3.wrapT = THREE.RepeatWrapping;
  mapBg3.repeat.set(32, 16);
  mapsBg['bg_3'] = mapBg3;
  
  const mapBg4 = textureLoader.load('../../textures/lava/lavatile.jpg');
  mapBg4.wrapS = mapBg4.wrapT = THREE.RepeatWrapping;
  mapBg4.repeat.set(8, 4);
  mapsBg['bg_4'] = mapBg4;
  
  const mapBg5 = textureLoader.load('../../textures/water.jpg');
  mapBg5.wrapS = mapBg5.wrapT = THREE.RepeatWrapping;
  mapBg5.repeat.set(8, 4);
  mapsBg['bg_5'] = mapBg5;
  
  const mapBg6 = textureLoader.load('../../textures/lava/cloud.png');
  mapBg6.wrapS = mapBg6.wrapT = THREE.RepeatWrapping;
  mapBg6.repeat.set(8, 4);
  mapsBg['bg_6'] = mapBg6;

  // 渲染背景
  materialBg = new THREE.MeshBasicMaterial({ map: mapBg0 });
  const geoBg = new THREE.PlaneBufferGeometry(4000, 2000);
  const meshBg = new THREE.Mesh(geoBg, materialBg);
  scene.add(meshBg);

  // 前景图片
  const fgImages = [
    '../../textures/disturb.jpg',
    '../../textures/sprite0.jpg',
    '../../textures/sprite0.png',
    '../../textures/lensflare/lensflare0.png',
    '../../textures/lensflare/lensflare0_alpha.png',
    '../../textures/sprite/ball.png',
    '../../textures/sprite/snowflake7_alpha.png'
  ];

  // 基于前景图片，构建预乘和非预乘两组贴图数组
  for(let i = 0; i < fgImages.length; i += 1) {
    // 无预乘
    const map = textureLoader.load(fgImages[i]);
    mapsFgNoPre.push(map);

    // 预乘
    const mapPre = textureLoader.load(fgImages[i]);
    mapPre.premultiplyAlpha = true; // rgb 是否预乘 alpha 通道值
    mapPre.needsUpdate = true;
    mapsFgPre.push(mapPre);
  }

  // 定义前景混合因子（源因子、目标因子）
  var src = [
    { name: 'Zero', constant: THREE.ZeroFactor },
    { name: 'One', constant: THREE.OneFactor },
    { name: 'SrcColor', constant: THREE.SrcColorFactor },
    { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor },
    { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor },
    { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor },
    { name: 'DstAlpha', constant: THREE.DstAlphaFactor },
    { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor },
    { name: 'DstColor', constant: THREE.DstColorFactor },
    { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor },
    { name: 'SrcAlphaSaturate', constant: THREE.SrcAlphaSaturateFactor }
  ];

  var dst = [
    { name: 'Zero', constant: THREE.ZeroFactor },
    { name: 'One', constant: THREE.OneFactor },
    { name: 'SrcColor', constant: THREE.SrcColorFactor },
    { name: 'OneMinusSrcColor', constant: THREE.OneMinusSrcColorFactor },
    { name: 'SrcAlpha', constant: THREE.SrcAlphaFactor },
    { name: 'OneMinusSrcAlpha', constant: THREE.OneMinusSrcAlphaFactor },
    { name: 'DstAlpha', constant: THREE.DstAlphaFactor },
    { name: 'OneMinusDstAlpha', constant: THREE.OneMinusDstAlphaFactor },
    { name: 'DstColor', constant: THREE.DstColorFactor },
    { name: 'OneMinusDstColor', constant: THREE.OneMinusDstColorFactor }
  ];

  // 前景对象几何体，标签几何体
  const geoFg = new THREE.PlaneBufferGeometry(100, 100);
  const geoLabel = new THREE.PlaneBufferGeometry(100, 25);

  // 构建前景材质列表；构建前景网格对象
  for(let i = 0; i < dst.length; i += 1) {
    const blendDst = dst[i];
    for(let j = 0; j < src.length; j += 1) {
      const blendSrc = src[j];

      const material = new THREE.MeshBasicMaterial({ map: currentMaps[currentMapIndex] });
      material.blending = THREE.CustomBlending; // 启用自定义混合
      material.blendSrc = blendSrc.constant;
      material.blendDst = blendDst.constant; // 设置混合因子
      material.blendEquation = THREE.AddEquation; // 设置混合方程

      // 定位
      const x = (j - src.length / 2) * 102;
      const y = (i - dst.length / 2) * 102 + 60;
      const z = 0;

      const mesh = new THREE.Mesh(geoFg, material);
      mesh.position.set(x, -y, z);
      mesh.matrixAutoUpdate = false; // 前景物体没有任何变化，因此无需自动进行矩阵变换
      mesh.updateMatrix(); // 初始化的矩阵变换
      scene.add(mesh);

      materials.push(material);
    }
  }

  // 构建源因子标签网格对象
  for(let i = 0; i < src.length; i += 1) {
    const blendSrc = src[i];

    const x = (i - src.length / 2) * 102;
    const y = (0 - dst.length / 2) * 102 + 60;
    const z = 0;

    const mesh = new THREE.Mesh(geoLabel, generateLabelMaterial(blendSrc.name, 'rgba(0, 150, 0, 1)'));
    mesh.position.set(x, -(y - 70), z);
    mesh.matrixAutoUpdate = false;
    mesh.updateMatrix();
    scene.add(mesh);
  }

  // 构建目标因子标签网格对象
  for(let i = 0; i < dst.length; i += 1) {
    const blendDst = dst[i];

    const x = (0 - src.length / 2) * 102 - 125;
    const y = (i - dst.length / 2) * 102 + 175;
    const z = 0;

    const mesh = new THREE.Mesh(geoLabel, generateLabelMaterial(blendDst.name, 'rgba(150, 0, 0, 1)'));
    mesh.position.set(x, -(y - 120), z);
    mesh.matrixAutoUpdate = false;
    mesh.updateMatrix();
    scene.add(mesh);
  }

  // 渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(devicePixelRatio);
  renderer.setSize(innerWidth, innerHeight);
  renderer.domElement.style.position = 'absolute';
  renderer.domElement.style.left = '215px';
  document.body.appendChild(renderer.domElement);

  // 事件绑定
  // 背景图片切换
  const bgWrap = document.getElementById('backgrounds').children;
  for(let i = 0; i < bgWrap.length; i += 1) {
    const bg = bgWrap[i];
    bg.addEventListener('click', handleBgClick, false);
  }

  // 前景图片切换
  const fgWrap = document.getElementById('images').children;
  for(let i = 0; i < fgWrap.length; i += 1) {
    const fg = fgWrap[i];
    fg.addEventListener('click', () => handleFgClick(i), false);
  }

  // 混合方程切换
  const eEquations = document.getElementsByClassName('equation');
  for(let i = 0; i < eEquations.length; i += 1) {
    const equation = eEquations[i];
    equation.addEventListener('click', handleEquationClick, false);
  }

  // 预乘开关
  const ePremultiplys = document.getElementsByClassName('premultiply');
  for(let i = 0; i < ePremultiplys.length; i += 1) {
    const premultiply = ePremultiplys[i];
    premultiply.addEventListener('click', handlePremultiplyClick, false);
  }
}
init();

// 启动函数
const animate = () => {
  requestAnimationFrame(animate);

  const time = Date.now() * 0.00025;

  const ox = time * -0.01 * materialBg.map.repeat.x % 1;
  const oy = time * -0.01 * materialBg.map.repeat.y % 1;

  materialBg.map.offset.set(ox, oy);

  renderer.render(scene, camera);
}
animate();